﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

using NLite.Reflection;
using System.Diagnostics;
using System.ComponentModel;

namespace NLite.Validation.DataAnnotations
{
    class EntityValidator<TEntity>:IValidator<TEntity>
    {
        public IValidationResult Validate(TEntity instance)
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;

            var propFailures =
                   from prop in instance.GetType().GetProperties(flags).Where(p=>p.CanRead)
                   from attribute in prop.GetAttributes<ValidationAttribute>(true)
                   where !attribute.IsValid(prop.GetValue(instance, null))
                   select new ValidationFailure { Type = ValidationFailureType.DataValidationFailure, ValidatorType = attribute.GetType(), MemberName = prop.Name, Message = FormatErrorMessage(prop, attribute) };
          
            var fieldFailures =
                   from prop in instance.GetType().GetFields(flags)
                   from attribute in prop.GetAttributes<ValidationAttribute>(true)
                   where !attribute.IsValid(prop.GetValue(instance))
                   select new ValidationFailure { Type = ValidationFailureType.DataValidationFailure, ValidatorType = attribute.GetType(), MemberName = prop.Name, Message = FormatErrorMessage(prop, attribute) };

            return new ValidationResult { Errors = propFailures.Union(fieldFailures).Cast < IValidationFailure>().ToArray() };
        }

        private static string FormatErrorMessage(MemberInfo prop, ValidationAttribute attribute)
        {
            var displayNameAttr = prop.GetAttribute<DisplayNameAttribute>(false);
            var displayName = displayNameAttr != null ? displayNameAttr.DisplayName : prop.Name;
            return attribute.FormatErrorMessage(displayName);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class ValidationResult : IValidationResult
    {
        internal ValidationResult() { }
        private List<IValidationFailure> errors = new List<IValidationFailure>();

        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<IValidationFailure> Errors
        {
            get { return errors; }
            internal set
            {
                errors.Clear();
                if (value.Count() > 0)
                    errors.AddRange(value);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsValid
        {
            get { return errors == null || errors.Count == 0; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberName"></param>
        /// <param name="errorMessage"></param>
        public void AddModelError(string memberName, string errorMessage)
        {
            AddModelError(ValidationFailureType.DataValidationFailure, null, memberName, errorMessage);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="memberName"></param>
        /// <param name="errorMessage"></param>
        public void AddModelError(int type, string memberName, string errorMessage)
        {
            AddModelError(type, null, memberName, errorMessage);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="validatorType"></param>
        /// <param name="memberName"></param>
        /// <param name="errorMessage"></param>
        public void AddModelError(Type validatorType,string memberName, string errorMessage)
        {
            AddModelError(ValidationFailureType.DataValidationFailure, validatorType, memberName, errorMessage);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="validatorType"></param>
        /// <param name="memberName"></param>
        /// <param name="errorMessage"></param>
        public void AddModelError(int type , Type validatorType, string memberName, string errorMessage)
        {
            if (type != ValidationFailureType.DataValidationFailure)
                errors.Add(new ValidationFailure { Type = type, ValidatorType = validatorType, MemberName = memberName, Message = errorMessage });
            else if (!string.IsNullOrEmpty(memberName) && errors.FirstOrDefault(p => p.Type == type && memberName == p.MemberName && p.ValidatorType == validatorType) == null)
                errors.Add(new ValidationFailure { Type = type, ValidatorType = validatorType, MemberName = memberName, Message = errorMessage });
        }

        /// <summary>
        /// 
        /// </summary>
        public static IValidationResult Empty
        {
            get { return new ValidationResult(); }
        }
    }

    [DebuggerDisplay("MemberName={MemberName},Message={Message}")]
    struct ValidationFailure : IValidationFailure
    {
        public int Type { get; internal set; }
        public Type ValidatorType { get; internal set; }
        public string Message { get; internal set; }
        public string MemberName { get; internal set; }
    }

   

   
    
}
